/* Copyright (C) 2004 - 2006  db4objects Inc.  http://www.db4o.com

This file is part of the db4o open source object database.

db4o is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL 
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.

db4o is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */
namespace Db4objects.Db4o.Nativequery.Optimization
{
	public class SODAQueryBuilder
	{
		private class SODAQueryVisitor : Db4objects.Db4o.Nativequery.Expr.IExpressionVisitor
		{
			private object _predicate;

			private Db4objects.Db4o.Query.IQuery _query;

			private Db4objects.Db4o.Query.IConstraint _constraint;

			internal SODAQueryVisitor(Db4objects.Db4o.Query.IQuery query, object predicate)
			{
				_query = query;
				_predicate = predicate;
			}

			public virtual void Visit(Db4objects.Db4o.Nativequery.Expr.AndExpression expression
				)
			{
				expression.Left().Accept(this);
				Db4objects.Db4o.Query.IConstraint left = _constraint;
				expression.Right().Accept(this);
				left.And(_constraint);
				_constraint = left;
			}

			public virtual void Visit(Db4objects.Db4o.Nativequery.Expr.BoolConstExpression expression
				)
			{
			}

			public virtual void Visit(Db4objects.Db4o.Nativequery.Expr.OrExpression expression
				)
			{
				expression.Left().Accept(this);
				Db4objects.Db4o.Query.IConstraint left = _constraint;
				expression.Right().Accept(this);
				left.Or(_constraint);
				_constraint = left;
			}

			public virtual void Visit(Db4objects.Db4o.Nativequery.Expr.ComparisonExpression expression
				)
			{
				Db4objects.Db4o.Query.IQuery subQuery = _query;
				System.Collections.IEnumerator fieldNameIterator = FieldNames(expression.Left());
				while (fieldNameIterator.MoveNext())
				{
					subQuery = subQuery.Descend((string)fieldNameIterator.Current);
				}
				Db4objects.Db4o.Nativequery.Optimization.ComparisonQueryGeneratingVisitor visitor
					 = new Db4objects.Db4o.Nativequery.Optimization.ComparisonQueryGeneratingVisitor
					(_predicate);
				expression.Right().Accept(visitor);
				_constraint = subQuery.Constrain(visitor.Value());
				Db4objects.Db4o.Nativequery.Expr.Cmp.ComparisonOperator op = expression.Op();
				if (op.Equals(Db4objects.Db4o.Nativequery.Expr.Cmp.ComparisonOperator.EQUALS))
				{
					return;
				}
				if (op.Equals(Db4objects.Db4o.Nativequery.Expr.Cmp.ComparisonOperator.IDENTITY))
				{
					_constraint.Identity();
					return;
				}
				if (op.Equals(Db4objects.Db4o.Nativequery.Expr.Cmp.ComparisonOperator.GREATER))
				{
					_constraint.Greater();
					return;
				}
				if (op.Equals(Db4objects.Db4o.Nativequery.Expr.Cmp.ComparisonOperator.SMALLER))
				{
					_constraint.Smaller();
					return;
				}
				if (op.Equals(Db4objects.Db4o.Nativequery.Expr.Cmp.ComparisonOperator.CONTAINS))
				{
					_constraint.Contains();
					return;
				}
				if (op.Equals(Db4objects.Db4o.Nativequery.Expr.Cmp.ComparisonOperator.STARTSWITH)
					)
				{
					_constraint.StartsWith(true);
					return;
				}
				if (op.Equals(Db4objects.Db4o.Nativequery.Expr.Cmp.ComparisonOperator.ENDSWITH))
				{
					_constraint.EndsWith(true);
					return;
				}
				throw new System.Exception("Can't handle constraint: " + op);
			}

			public virtual void Visit(Db4objects.Db4o.Nativequery.Expr.NotExpression expression
				)
			{
				expression.Expr().Accept(this);
				_constraint.Not();
			}

			private System.Collections.IEnumerator FieldNames(Db4objects.Db4o.Nativequery.Expr.Cmp.FieldValue
				 fieldValue)
			{
				Db4objects.Db4o.Foundation.Collection4 coll = new Db4objects.Db4o.Foundation.Collection4
					();
				Db4objects.Db4o.Nativequery.Expr.Cmp.IComparisonOperand curOp = fieldValue;
				while (curOp is Db4objects.Db4o.Nativequery.Expr.Cmp.FieldValue)
				{
					Db4objects.Db4o.Nativequery.Expr.Cmp.FieldValue curField = (Db4objects.Db4o.Nativequery.Expr.Cmp.FieldValue
						)curOp;
					coll.Prepend(curField.FieldName());
					curOp = curField.Parent();
				}
				return coll.GetEnumerator();
			}
		}

		public virtual void OptimizeQuery(Db4objects.Db4o.Nativequery.Expr.IExpression expr
			, Db4objects.Db4o.Query.IQuery query, object predicate)
		{
			expr.Accept(new Db4objects.Db4o.Nativequery.Optimization.SODAQueryBuilder.SODAQueryVisitor
				(query, predicate));
		}
	}
}
